package cn.zly.spring.beans.factory.xml;

import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.XmlUtil;
import cn.zly.spring.beans.PropertyValue;
import cn.zly.spring.beans.factory.BeansException;
import cn.zly.spring.beans.factory.config.BeanDefinition;
import cn.zly.spring.beans.factory.config.BeanReference;
import cn.zly.spring.beans.factory.support.AbstractBeanDefinitionReader;
import cn.zly.spring.beans.factory.support.BeanDefinitionRegistry;
import cn.zly.spring.beans.factory.xml.enums.BeanTypeXmlElementEnums;
import cn.zly.spring.core.io.Resource;
import cn.zly.spring.core.io.ResourceLoader;
import org.junit.platform.commons.util.StringUtils;
import org.springframework.beans.factory.support.AbstractBeanFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import java.io.IOException;
import java.io.InputStream;

/**
 * @author zhanglianyong
 * 2025-01-02 22:31
 */
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {


    public XmlBeanDefinitionReader(BeanDefinitionRegistry registry, ResourceLoader resourceLoader) {
        super(registry, resourceLoader);
    }

    public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
        super(registry);
    }

    @Override
    public void loadBeanDefinitions(Resource resource) throws Exception {
        InputStream inputStream = resource.getInputStream();
        doLoadBeanDefinitions(inputStream);
    }

    private void doLoadBeanDefinitions(InputStream inputStream) throws ClassNotFoundException {
        Document doc = XmlUtil.readXML(inputStream);
        Element root = doc.getDocumentElement();

        NodeList childNodes = root.getChildNodes();
        for (int i = 0; i < childNodes.getLength(); i++) {
            if (!(childNodes.item(i) instanceof Element)) {
                continue;
            }

            if (!BeanTypeXmlElementEnums.BEAN.getCode().equals(childNodes.item(i).getNodeName())) {
                continue;
            }
            Element item = (Element)childNodes.item(i);
            String id = item.getAttribute(BeanTypeXmlElementEnums.ID.getCode());
            String name = item.getAttribute(BeanTypeXmlElementEnums.NAME.getCode());
            String beanClassName = item.getAttribute(BeanTypeXmlElementEnums.CLASS.getCode());
            Class<?> beanClass = Class.forName(beanClassName);

            String beanName = StringUtils.isNotBlank(id) ? id : name;
            if (StrUtil.isEmpty(beanName)) {
                beanName = StrUtil.lowerFirst(beanClass.getSimpleName());
            }

            BeanDefinition beanDefinition = new BeanDefinition(beanClass);
            // 读取属性并填充
            for (int j = 0; j < item.getChildNodes().getLength(); j++) {
                if (!(item.getChildNodes().item(j) instanceof Element)) continue;
                if (!"property".equals(item.getChildNodes().item(j).getNodeName())) continue;
                // 解析标签：property
                Element property = (Element) item.getChildNodes().item(j);
                String attrName = property.getAttribute("name");
                String attrValue = property.getAttribute("value");
                String attrRef = property.getAttribute("ref");
                // 获取属性值：引入对象、值对象
                Object value = StrUtil.isNotEmpty(attrRef) ? new BeanReference(attrRef) : attrValue;
                // 创建属性信息
                PropertyValue propertyValue = new PropertyValue(attrName, value);
                beanDefinition.getPropertyValues().addPropertyValue(propertyValue);
            }
            if (getRegistry().containBeanDefinition(beanName)) {
                throw new BeansException("bean name:" + beanName + " is exists");
            }
            getRegistry().registerBeanDefinition(beanName, beanDefinition);
        }
    }

    @Override
    public void loadBeanDefinitions(Resource... resources) throws Exception {
        for (Resource resource : resources) {
            try {
                doLoadBeanDefinitions(resource.getInputStream());
            } catch (IOException e) {
                throw new BeansException("IOException parsing XML document from " + resource, e);
            }
        }
    }

    @Override
    public void loadBeanDefinitions(String location) throws Exception {
        ResourceLoader resourceLoader = getResourceLoader();
        Resource resource = resourceLoader.getResource(location);
        loadBeanDefinitions(resource);
    }
}
